#include "Task.hpp"
#include <vector>
#include <string>
#include <unistd.h>
#include <cstdlib>
#include <cassert>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>

const int processnum = 10;
std::vector<task_t> tasks;

// 先描述--用类进行封装
class channel{
public:
    channel(int cmdfd, pid_t slaverid, const std::string processname)
    :_cmdfd(cmdfd),_slaverid(slaverid),_processname(processname)
    {}

public:
    int _cmdfd;               // 父进程发送任务的文件描述符
    pid_t _slaverid;          // 子进程的id
    std::string _processname; // 子进程的名字--方便后面打日志
};


void slaver(){
    // read(0)-->重定向后从文件描述符0中读取 不再有管道的概念
    while(true){
        int cmdcode = 0;
        // 如果父进程不给子进程发送数据，子进程就会阻塞等待
        int n = read(0, &cmdcode, sizeof(int));
        if(n==sizeof(int)){
            // test
            std::cout << "slaver say@ get a command: " << getpid() << " : cmdcode: " << cmdcode << std::endl;

            // 执行cmdcode对应的任务列表
            if(cmdcode>=0&&cmdcode<tasks.size()){
                tasks[cmdcode]();
            }
        }
        else if(n==0){
            break;
        }
    }
}


// 传参规范：
// 输入性参数：const &
// 输出型参数：*
// 输入输出行参数：&
void InitprocessPool(std::vector<channel> *channels){
    for (int i = 0; i < processnum; i++){
        int pipefd[2];       // 临时空间存放管道的文件描述符
        int n = pipe(pipefd);
        assert(!n);          // 断言--判断管道是否创建成功

        pid_t id = fork();   // 创建子进程
        if(id==0){
            //child
            close(pipefd[1]);        // 子进程关闭写端
            dup2(pipefd[0], 0);      // 输入重定向
            close(pipefd[0]);        // 重定向后关闭原本的读端

            slaver();
            exit(0);
        }
        // father
        close(pipefd[0]);            // 父进程关闭读端

        // 添加channel字段
        std::string name = "process-" + std::to_string(i);
        channels->push_back(channel(pipefd[1], id, name));
    }
}


// test
void Debug(const std::vector<channel> &channels){
    for(auto& process: channels){
        std::cout << process._cmdfd << " " << process._slaverid << " " << process._processname << std::endl;
    }
}


void ctrlSlaver(const std::vector<channel> &channels){
    int which = 0;
    //int cnt = 5;
    while (true)
    {
        // 2.1.选择任务
        int cmdcode = rand() % tasks.size();

        // 2.2.选择子进程
        // 随机选取方式
        // int processpos = rand() % channels.size();

        // test
        std::cout << "father say: " << " cmdcode: " << cmdcode << " already send to " 
                  << channels[which]._slaverid << " process name: "
                  << channels[which]._processname << std::endl;
        
        // 2.3.发送任务
        write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode));

        // 轮询选取方式
        which++;
        which %= channels.size();
 
        //cnt--;
        sleep(1);
    }
}


void Quitprocess(const std::vector<channel> &channels){
    for(const auto& process : channels){
        // 父进程写端关闭后，子进程读取到文件末尾后函数退出，然后执行exit子进程退出
        close(process._cmdfd);
    }
    for(const auto& process : channels){
        // 父进程回收子进程
        waitpid(process._slaverid, nullptr, 0);
    }
}


int main(){
    // 初始化任务列表
    LoadTask(&tasks);

    // 种一个随机数种子
    srand(time(nullptr) ^ getpid() ^ 1023);

    // 再组织--用数组存储创建的每一个子进程
    std::vector<channel> channels;

    // 1.初始化
    InitprocessPool(&channels);
    Debug(channels);

    // 2.开始控制子进程
    ctrlSlaver(channels);

    // 3.回收清理
    Quitprocess(channels);

    sleep(100);
    return 0;
}
